package top.goldenyear.porpoise.framework.common.util.enums;

import cn.hutool.v7.core.array.ArrayUtil;
import cn.hutool.v7.core.collection.CollUtil;
import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.core.util.EnumUtil;
import java.util.*;
import java.util.stream.Collectors;
import top.goldenyear.porpoise.common.core.model.EnumOption;

/**
 * 枚举类使用工具类
 *
 * @author airhead
 */
public class EnumUtils {

  public static final String TEXT = "text";
  public static final String VALUE = "value";
  private static final LinkedHashMap<String, EnumOption> EMPTY_BEAN_HASHMAP = new LinkedHashMap<>();
  private static final LinkedHashMap<String, Enum<?>> EMPTY_OBJECT_HASHMAP = new LinkedHashMap<>();
  // 存放 enum.name为key 值是Enum的map
  private static final HashMap<String, LinkedHashMap<String, ? extends Enum<?>>>
      ENUM_NAME_OBJECT_MAP = new HashMap<>();
  // 存放 enum.value为key, 值是Enum的map
  private static final HashMap<String, LinkedHashMap<String, ? extends Enum<?>>> ENUM_V_OBJECT_MAP =
      new HashMap<>();

  // 存放 t_value和t_text 分别为key, 值是EnumOption的 map
  private static final HashMap<String, LinkedHashMap<String, EnumOption>> ENUM_TV_BEAN_MAP =
      new HashMap<>();
  // 存放 enum.name 分别为key, 值是EnumOption的 map
  private static final HashMap<String, LinkedHashMap<String, EnumOption>> ENUM_NAME_BEAN_MAP =
      new HashMap<>();

  /**
   * 将枚举类 处理成map
   *
   * @param clazz
   * @return
   */
  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> addToTvBeanMap(
      Class<E> clazz) {
    return addToTvBeanMap(clazz, TEXT, VALUE);
  }

  /**
   * 添加到text和value作为key的map中管理
   *
   * @param clazz
   * @param nameAttr
   * @param valueAttr
   * @return
   */
  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> addToTvBeanMap(
      Class<E> clazz, String nameAttr, String valueAttr) {
    List<Object> texts = EnumUtil.getFieldValues(clazz, nameAttr);
    boolean hasValueAttr = valueAttr != null && !valueAttr.trim().isEmpty();
    List<String> names = null;
    List<Object> values = null;
    if (hasValueAttr) {
      values = EnumUtil.getFieldValues(clazz, valueAttr);
    } else {
      names = EnumUtil.getNames(clazz);
    }
    if (CollUtil.isEmpty(texts)) {
      return EMPTY_BEAN_HASHMAP;
    }

    // 组合beanMap
    LinkedHashMap<String, EnumOption> tvMap = new LinkedHashMap<>();
    int size = texts.size();
    EnumOption bean;
    for (int i = 0; i < size; i++) {
      bean = new EnumOption(texts.get(i).toString(), hasValueAttr ? values.get(i) : names.get(i));
      tvMap.put(getValueKey(bean.getValue()), bean);
      tvMap.put(getTextKey(bean.getName()), bean);
    }
    ENUM_TV_BEAN_MAP.put(clazz.getName(), tvMap);
    return tvMap;
  }

  /**
   * 添加到text和value作为key的map中管理
   *
   * @param clazz
   * @return
   */
  public static LinkedHashMap<String, ? extends Enum<?>> addToVEnumObjectMap(
      Class<? extends Enum<?>> clazz) {
    return addToVEnumObjectMap(clazz, TEXT, VALUE);
  }

  /**
   * 添加value作为key的map中管理
   *
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static LinkedHashMap<String, ? extends Enum<?>> addToVEnumObjectMap(
      Class<? extends Enum<?>> clazz, String textAttrName, String valueAttrName) {
    List<Object> texts = EnumUtil.getFieldValues(clazz, textAttrName);
    if (CollUtil.isEmpty(texts)) {
      return EMPTY_OBJECT_HASHMAP;
    }
    boolean hasValueAttr = valueAttrName != null && !valueAttrName.trim().isEmpty();
    List<Object> values = null;
    List<String> names = null;
    if (hasValueAttr) {
      values = EnumUtil.getFieldValues(clazz, valueAttrName);
    } else {
      names = EnumUtil.getNames(clazz);
    }
    LinkedHashMap<String, Enum<?>> emMap = new LinkedHashMap<>();
    Enum<?>[] es = clazz.getEnumConstants();
    int esSize = es.length;
    for (int i = 0; i < esSize; i++) {
      emMap.put(getValueKey((hasValueAttr ? values.get(i) : names.get(i))), es[i]);
    }
    ENUM_V_OBJECT_MAP.put(clazz.getName(), emMap);
    return emMap;
  }

  /**
   * 添加到name作为key的map中管理
   *
   * @param clazz
   * @return
   */
  public static LinkedHashMap<String, ? extends Enum<?>> addToNameEnumObjectMap(
      Class<? extends Enum<?>> clazz) {
    Enum<?>[] es = clazz.getEnumConstants();
    if (es == null || es.length == 0) {
      return EMPTY_OBJECT_HASHMAP;
    }
    LinkedHashMap<String, Enum<?>> emMap = new LinkedHashMap<>();
    for (Enum<?> e : es) {
      emMap.put(e.name(), e);
    }
    ENUM_NAME_OBJECT_MAP.put(clazz.getName(), emMap);
    return emMap;
  }

  /**
   * 通过value得到text
   *
   * @param clazz
   * @param value
   * @return
   */
  public static <E extends Enum<E>> String getTextByValue(Class<E> clazz, Object value) {
    EnumOption bean = getEnumOptionByValue(clazz, value);
    return bean == null ? null : bean.getName();
  }

  /**
   * 通过value得到bean
   *
   * @param clazz
   * @param value
   * @return
   */
  public static <E extends Enum<E>> EnumOption getEnumOptionByValue(Class<E> clazz, Object value) {
    if (value == null) {
      return null;
    }
    LinkedHashMap<String, EnumOption> beanMap = ENUM_TV_BEAN_MAP.get(clazz.getName());
    if (beanMap == null) {
      beanMap = addToTvBeanMap(clazz);
      if (beanMap == null) {
        return null;
      }
    }
    return beanMap.get(getValueKey(value));
  }

  /**
   * 通过value得到Enum对象
   *
   * @param clazz
   * @param value
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T extends Enum<T>> T getEnumByValue(Class<T> clazz, Object value) {
    if (value == null) {
      return null;
    }
    LinkedHashMap<String, ? extends Enum<?>> objMap = ENUM_V_OBJECT_MAP.get(clazz.getName());
    if (objMap == null) {
      objMap = addToVEnumObjectMap(clazz);
      if (objMap == null) {
        return null;
      }
    }
    return (T) objMap.get(getValueKey(value));
  }

  /**
   * 通过name得到Enum对象
   *
   * @param clazz
   * @param name
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T extends Enum<T>> T getEnumByName(Class<T> clazz, String name) {
    if (StrUtil.isBlank(name)) {
      return null;
    }
    LinkedHashMap<String, ? extends Enum<?>> objMap = ENUM_NAME_OBJECT_MAP.get(clazz.getName());
    if (objMap == null) {
      objMap = addToNameEnumObjectMap(clazz);
      if (objMap == null) {
        return null;
      }
    }
    return (T) objMap.get(name);
  }

  /**
   * 通过text得到bean
   *
   * @param clazz
   * @param text
   * @return
   */
  public static <E extends Enum<E>> EnumOption getEnumOptionByText(Class<E> clazz, String text) {
    if (StrUtil.isBlank(text)) {
      return null;
    }
    LinkedHashMap<String, EnumOption> beanMap = ENUM_TV_BEAN_MAP.get(clazz.getName());
    if (beanMap == null) {
      beanMap = addToTvBeanMap(clazz);
      if (beanMap == null) {
        return null;
      }
    }
    return beanMap.get(getTextKey(text));
  }

  /**
   * 获取Enum List数据
   *
   * @param clazz
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumNameBeanList(Class<E> clazz) {
    return getEnumNameBeanList(clazz, TEXT, VALUE);
  }

  /**
   * 获取Enum List数据
   *
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumNameBeanList(
      Class<E> clazz, String textAttrName, String valueAttrName) {
    LinkedHashMap<String, EnumOption> beanMap = ENUM_NAME_BEAN_MAP.get(clazz.getName());
    if (beanMap == null) {
      beanMap = addToNameBeanMap(clazz, textAttrName, valueAttrName);
      if (beanMap == null) {
        return Collections.emptyList();
      }
    }
    return new ArrayList<>(beanMap.values());
  }

  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> getEnumNameBeanMap(
      Class<E> clazz) {
    return getEnumNameBeanMap(clazz, TEXT, VALUE);
  }

  /**
   * 得到
   *
   * @param <E>
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> getEnumNameBeanMap(
      Class<E> clazz, String textAttrName, String valueAttrName) {
    String className = clazz.getSimpleName();
    LinkedHashMap<String, EnumOption> nameBeanMap = ENUM_NAME_BEAN_MAP.get(className);
    if (nameBeanMap == null) {
      nameBeanMap = addToNameBeanMap(clazz, textAttrName, valueAttrName);
    }
    return nameBeanMap;
  }

  /**
   * 添加用enum name作为key的beanMap
   *
   * @param <E>
   * @param clazz
   * @return
   */
  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> addToNameBeanMap(
      Class<E> clazz) {
    return addToNameBeanMap(clazz, TEXT, VALUE);
  }

  /**
   * 添加用enum name作为key的beanMap
   *
   * @param <E>
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> addToNameBeanMap(
      Class<E> clazz, String textAttrName, String valueAttrName) {
    List<String> names = EnumUtil.getNames(clazz);
    if (CollUtil.isEmpty(names)) {
      return EMPTY_BEAN_HASHMAP;
    }
    List<Object> texts = EnumUtil.getFieldValues(clazz, textAttrName);
    boolean hasValueAttr = valueAttrName != null && valueAttrName.trim().length() > 0;
    List<Object> values = null;
    if (hasValueAttr) {
      values = EnumUtil.getFieldValues(clazz, valueAttrName);
    }
    // 组合beanMap
    LinkedHashMap<String, EnumOption> beanMap = new LinkedHashMap<String, EnumOption>();
    int size = names.size();
    for (int i = 0; i < size; i++) {
      beanMap.put(
          names.get(i),
          new EnumOption(texts.get(i).toString(), hasValueAttr ? values.get(i) : names.get(i)));
    }
    ENUM_NAME_BEAN_MAP.put(clazz.getSimpleName(), beanMap);
    return beanMap;
  }

  /**
   * 数据源 下拉列表 radio checkbox等 都可以
   *
   * @param <E>
   * @param clazz
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumOptionList(Class<E> clazz) {
    return getEnumNameBeanList(clazz);
  }

  /**
   * 数据源 下拉列表 radio checkbox等 都可以
   *
   * @param <E>
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumOptionList(
      Class<E> clazz, String textAttrName, String valueAttrName) {
    return getEnumNameBeanList(clazz, textAttrName, valueAttrName);
  }

  /**
   * 数据源 下拉列表 radio checkbox等 都可以
   *
   * @param <E>
   * @param clazz
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumOptionListWithout(
      Class<E> clazz, Object... withoutValues) {
    return getEnumOptionListWithout(clazz, TEXT, VALUE, withoutValues);
  }

  /**
   * 数据源 下拉列表 radio checkbox等 都可以
   *
   * @param <E>
   * @param clazz
   * @param textAttrName
   * @param valueAttrName
   * @return
   */
  public static <E extends Enum<E>> List<EnumOption> getEnumOptionListWithout(
      Class<E> clazz, String textAttrName, String valueAttrName, Object... withoutValues) {
    List<EnumOption> beans = getEnumNameBeanList(clazz, textAttrName, valueAttrName);
    if (!beans.isEmpty() && withoutValues != null && withoutValues.length > 0) {
      return beans.stream()
          .filter(bean -> (!ArrayUtil.contains(withoutValues, bean.getValue())))
          .collect(Collectors.toList());
    }
    return beans;
  }

  public static LinkedHashMap<String, EnumOption> getEnumOptionMap(String className) {
    return ENUM_TV_BEAN_MAP.get(className);
  }

  public static <E extends Enum<E>> LinkedHashMap<String, EnumOption> getEnumOptionMap(
      Class<E> clazz) {
    return getEnumOptionMap(clazz.getName());
  }

  private static String getValueKey(Object value) {
    return "v_" + value;
  }

  private static String getTextKey(String text) {
    return "t_" + text;
  }
}
